<?php

namespace maoxuner\LaravelJsonRpc\Entities;

use Illuminate\Support\Facades\Validator;
use maoxuner\LaravelJsonRpc\Exceptions\BaseException;
use maoxuner\LaravelJsonRpc\Exceptions\InternalException;
use maoxuner\LaravelJsonRpc\Exceptions\MethodException;
use maoxuner\LaravelJsonRpc\Exceptions\NonRpcException;
use maoxuner\LaravelJsonRpc\Exceptions\ParamsException;
use maoxuner\LaravelJsonRpc\Exceptions\ParseException;
use maoxuner\LaravelJsonRpc\Exceptions\RequestException;
use maoxuner\LaravelJsonRpc\Exceptions\RpcException;
use maoxuner\LaravelJsonRpc\Exceptions\ServerException;
use Throwable;

class ErrorObject extends EntityObject
{
    public int $code;

    public string $message;

    public array $data = [];

    public static function build(Throwable $throwable): static
    {
        $exception = $throwable instanceof BaseException
            ? $throwable
            : InternalException::build($throwable);

        return new static([
            'code' => $exception->getCode(),
            'message' => $exception->getMessage(),
            'data' => $exception->toArray(),
        ]);
    }

    /**
     * @throws ServerException
     * @throws \Illuminate\Validation\ValidationException
     */
    public static function validate(array $data): array
    {
        $validator = Validator::make($data, [
            'message' => 'present|string',
            'code' => 'present|integer',
            'data' => 'array',
        ]);

        if ($validator->fails()) {
            throw (new ServerException('Response error'))
                ->fill($validator->getMessageBag()->getMessages());
        }

        return $validator->validated();
    }

    public function exception(): BaseException
    {
        if ($this->code >= -32768 && $this->code <= -32000) {
            if ($this->code === -32700) {
                $exception = new ParseException;
            } elseif ($this->code === -32600) {
                $exception = new RequestException;
            } elseif ($this->code === -32601) {
                $exception = new MethodException;
            } elseif ($this->code === -32602) {
                $exception = new ParamsException;
            } elseif ($this->code === -32603) {
                $exception = new InternalException;
            } elseif ($this->code >= -32099) {
                $exception = new ServerException($this->message, $this->code);
            } else {
                $exception = new RpcException($this->message, $this->code);
            }
        } else {
            $exception = new NonRpcException($this->message, $this->code);
        }

        return $exception->fill($this->data);
    }

    public function toArray()
    {
        $data = [
            'code' => $this->code,
            'message' => $this->message,
        ];
        if ($this->data) {
            $data['data'] = $this->data;
        }

        return $data;
    }
}
